#include "ml_i2c.h"

#define _i2c_read_sda()                                                        \
  ((NCHD12_PORT_PORT->DIN31_0 & NCHD12_PORT_SDA_PIN) > 0 ? 0x01 : 0x00)
#define _i2c_sda_high() (NCHD12_PORT_PORT->DOUTSET31_0 = NCHD12_PORT_SDA_PIN)
#define _i2c_sda_low() (NCHD12_PORT_PORT->DOUTCLR31_0 = NCHD12_PORT_SDA_PIN)
#define _i2c_scl_high() (NCHD12_PORT_PORT->DOUTSET31_0 = NCHD12_PORT_SCL_PIN)
#define _i2c_scl_low() (NCHD12_PORT_PORT->DOUTCLR31_0 = NCHD12_PORT_SCL_PIN)
#define _NCHD12_SDA PA26
#define _NCHD12_SCL PA25
/**********************************************************************************************
 *名    称：static void I2C_GPIO_Configuration(void)
 *
 *参    数：无
 *
 *返 回 值：无
 *
 *描    述：I2C IO口配置
 *********************************************************************************************/
static void I2C_GPIO_Configuration(void) {
  _i2c_sda_high();
  _i2c_scl_high();
  _bsp_analog_i2c_stop();
}

void _i2c_sda_out(void) {
  DL_GPIO_initDigitalOutput(NCHD12_PORT_SDA_IOMUX);
  DL_GPIO_enableOutput(NCHD12_PORT_PORT, NCHD12_PORT_SDA_PIN);
}
void _i2c_sda_in(void) {
  // DL_GPIO_initDigitalInput(NCHD12_PORT_SDA_IOMUX);
  DL_GPIO_initDigitalInputFeatures(
      NCHD12_PORT_SDA_IOMUX, DL_GPIO_INVERSION_DISABLE,
      DL_GPIO_RESISTOR_PULL_UP, DL_GPIO_HYSTERESIS_DISABLE,
      DL_GPIO_WAKEUP_DISABLE);
}

static void _analog_i2c_delay() {
  uint8_t i;

  /*　
          下面的时间是通过安富莱AX-Pro逻辑分析仪测试得到的。
          CPU主频72MHz时，在内部Flash运行, MDK工程不优化
          循环次数为10时，SCL频率 = 205KHz
          循环次数为7时，SCL频率 = 347KHz，
SCL高电平时间1.5us，SCL低电平时间2.87us 循环次数为5时，SCL频率 = 421KHz，
SCL高电平时间1.25us，SCL低电平时间2.375us

IAR工程编译效率高，不能设置为7
  */
  //  for (i = 0; i < 10; i++)
  //    ;
  delay_us(10);
}

void _bsp_analog_i2c_start(void) {
  /*    _____
   *SDA      \_____________
   *    __________
   *SCL           \________
   */
  _i2c_sda_out();
  _i2c_sda_high();
  _analog_i2c_delay();
  _i2c_scl_high();
  _analog_i2c_delay();
  _i2c_sda_low();
  _analog_i2c_delay();
  _i2c_scl_low();
  _analog_i2c_delay();
}

void _bsp_analog_i2c_stop(void) {
  /*               _______
   *SDA __________/
   *          ____________
   *SCL _____/
   */
  _i2c_sda_out();
  _i2c_scl_low(); // add
  _i2c_sda_low();
  _i2c_scl_high();
  _analog_i2c_delay();
  _i2c_sda_high();
  _analog_i2c_delay();
}

uint8_t _bsp_analog_i2c_wait_ack(void) {
  uint32_t timeout = 0;
  _i2c_scl_low();
  _i2c_sda_high();
  _i2c_sda_in();
  _analog_i2c_delay();
  _i2c_scl_high();
  _analog_i2c_delay();
  while (_i2c_read_sda()) {
    timeout++;
    if (timeout > 2000) // 2000
    {
      _bsp_analog_i2c_stop();
      return 0;
    }
  }
  _i2c_scl_low();
  _analog_i2c_delay();
  return 1;
}

void _bsp_analog_i2c_ack(void) {
  /*           ____
   *SCL ______/    \______
   *    ____         _____
   *SDA     \_______/
   */
  _i2c_sda_out();
  _i2c_scl_low();
  _i2c_sda_high();
  _analog_i2c_delay();
  _i2c_sda_low();
  _analog_i2c_delay();
  _i2c_scl_high();
  _analog_i2c_delay();
  _i2c_scl_low();
  _analog_i2c_delay();
  _i2c_sda_high();
}

void _bsp_analog_i2c_nack(void) {
  /*           ____
   *SCL ______/    \______
   *    __________________
   *SDA
   */
  _i2c_scl_low();
  _i2c_sda_out();
  _i2c_sda_high();
  _analog_i2c_delay();
  _i2c_scl_high();
  _analog_i2c_delay();
  _i2c_scl_low();
  _analog_i2c_delay();
}

void _bsp_analog_i2c_send_byte(uint8_t data) {
  uint8_t i;
  _i2c_sda_out();
  _i2c_scl_low();
  for (i = 0; i < 8; i++) {
    if ((data & 0x80) >> 7)
      _i2c_sda_high();
    else
      _i2c_sda_low();
    data <<= 1;
    _analog_i2c_delay();
    _i2c_scl_high();
    _analog_i2c_delay();
    _i2c_scl_low();
  }
  _bsp_analog_i2c_wait_ack();
}

void _bsp_analog_i2c_send_byte_nask(uint8_t data) {
  uint8_t i;
  _i2c_sda_out();
  _i2c_scl_low();
  for (i = 0; i < 8; i++) {
    if ((data & 0x80) >> 7)
      _i2c_sda_high();
    else
      _i2c_sda_low();
    data <<= 1;
    _analog_i2c_delay();
    _i2c_scl_high();
    _analog_i2c_delay();
    _i2c_scl_low();
  }
}

uint8_t _bsp_analog_i2c_read_byte(void) {
  _i2c_sda_high();
  _i2c_sda_in();
  uint8_t i, data = 0;
  for (i = 0; i < 8; i++) {
    data <<= 1;
    _i2c_scl_high();
    _analog_i2c_delay();
    if (_i2c_read_sda()) {
      data++;
    }
    _i2c_scl_low();
    _analog_i2c_delay();
  }
  return data;
}

uint8_t i2c_CheckDevice(uint8_t _Address) {

  uint8_t ucAck;
  I2C_GPIO_Configuration();
  _bsp_analog_i2c_start(); /* 发送启动信号 */
  /* 发送设备地址+读写控制bit（0 = w， 1 = r) bit7 先传 */
  _bsp_analog_i2c_send_byte_nask(_Address | I2C_WR);
  ucAck = _bsp_analog_i2c_wait_ack(); /* 检测设备的ACK应答 */
  _bsp_analog_i2c_stop();             /* 发送停止信号 */
  return ucAck;
}
////////////////////////////////////////////////
// 移植江协I2C
void SDA_OUT(void) {
  DL_GPIO_initDigitalOutput(GPIO_sda_PIN_0_IOMUX);
  DL_GPIO_setPins(GPIO_sda_PORT, GPIO_sda_PIN_0_PIN);
  DL_GPIO_enableOutput(GPIO_sda_PORT, GPIO_sda_PIN_0_PIN);
}
// 关闭SDA引脚（输入）
void SDA_IN(void) {

  DL_GPIO_initDigitalInputFeatures(
      GPIO_sda_PIN_0_IOMUX, DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
      DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
}

/*引脚配置层*/

/**
 * 函    数：I2C写SCL引脚电平
 * 参    数：BitValue 协议层传入的当前需要写入SCL的电平，范围0~1
 * 返 回 值：无
 * 注意事项：此函数需要用户实现内容，当BitValue为0时，需要置SCL为低电平，当BitValue为1时，需要置SCL为高电平
 */
void MyI2C_W_SCL(uint8_t BitValue) {
  if (BitValue)
    DL_GPIO_setPins(GPIO_scl_PORT, GPIO_scl_PIN_1_PIN);
  else
    DL_GPIO_clearPins(GPIO_scl_PORT, GPIO_scl_PIN_1_PIN);
  delay_us(8); // 延时8us，防止时序频率超过要求
}

/**
 * 函    数：I2C写SDA引脚电平
 * 参    数：BitValue 协议层传入的当前需要写入SDA的电平，范围0~0xFF
 * 返 回 值：无
 * 注意事项：此函数需要用户实现内容，当BitValue为0时，需要置SDA为低电平，当BitValue非0时，需要置SDA为高电平
 */
void MyI2C_W_SDA(uint8_t BitValue) {
  SDA_OUT();
  if (BitValue)
    DL_GPIO_setPins(GPIO_sda_PORT, GPIO_sda_PIN_0_PIN);
  else
    DL_GPIO_clearPins(GPIO_sda_PORT, GPIO_sda_PIN_0_PIN);
  delay_us(8); // 延时8us，防止时序频率超过要求
}

/**
 * 函    数：I2C读SDA引脚电平
 * 参    数：无
 * 返 回 值：协议层需要得到的当前SDA的电平，范围0~1
 * 注意事项：此函数需要用户实现内容，当前SDA为低电平时，返回0，当前SDA为高电平时，返回1
 */
uint8_t MyI2C_R_SDA(void) {
  uint8_t b;
  uint32_t BitValue;
  SDA_IN();
  BitValue = DL_GPIO_readPins(GPIO_sda_PORT, GPIO_sda_PIN_0_PIN); // 读取SDA电平
  {
    if (BitValue)
      b = 1;
    else
      b = 0;
  }
  delay_us(8); // 延时8us，防止时序频率超过要求
  return b;    // 返回SDA电平
}

/**
 * 函    数：I2C初始化
 * 参    数：无
 * 返 回 值：无
 * 注意事项：此函数需要用户实现内容，实现SCL和SDA引脚的初始化
 */
void MyI2C_Init(void) {
  // SYSCFG_DL_GPIO_init();
  /*设置默认电平*/
  DL_GPIO_setPins(GPIOA,
                  GPIO_sda_PIN_0_PIN | GPIO_scl_PIN_1_PIN); //
  //         设置PA8和PA9引脚初始化后默认为高电平（释放总线状态）
}

/*协议层*/

/**
 * 函    数：I2C起始
 * 参    数：无
 * 返 回 值：无
 */
void MyI2C_Start(void) {
  SDA_OUT();
  MyI2C_W_SDA(1); // 释放SDA，确保SDA为高电平
  MyI2C_W_SCL(1); // 释放SCL，确保SCL为高电平
  MyI2C_W_SDA(0); // 在SCL高电平期间，拉低SDA，产生起始信号
  MyI2C_W_SCL(0); // 起始后拉低SCL，为了占用总线，方便总线时序的拼接
}

/**
 * 函    数：I2C终止
 * 参    数：无
 * 返 回 值：无
 */
void MyI2C_Stop(void) {
  SDA_OUT();
  MyI2C_W_SDA(0); // 拉低SDA，确保SDA为低电平
  MyI2C_W_SCL(1); // 释放SCL，使SCL呈现高电平
  MyI2C_W_SDA(1); // 在SCL高电平期间，释放SDA，产生终止信号
}

/**
 * 函    数：I2C发送一个字节
 * 参    数：Byte 要发送的一个字节数据，范围：0x00~0xFF
 * 返 回 值：无
 */
void MyI2C_SendByte(uint8_t Byte) {
  SDA_OUT();
  uint8_t i;
  for (i = 0; i < 8; i++) // 循环8次，主机依次发送数据的每一位
  {
    MyI2C_W_SDA(
        Byte &
        (0x80 >> i)); // 使用掩码的方式取出Byte的指定一位数据并写入到SDA线
    MyI2C_W_SCL(1);   // 释放SCL，从机在SCL高电平期间读取SDA
    MyI2C_W_SCL(0);   // 拉低SCL，主机开始发送下一位数据
  }
}

/**
 * 函    数：I2C接收一个字节
 * 参    数：无
 * 返 回 值：接收到的一个字节数据，范围：0x00~0xFF
 */
uint8_t MyI2C_ReceiveByte(void) {
  SDA_OUT();
  uint8_t i, Byte = 0x00; // 定义接收的数据，并赋初值0x00
  MyI2C_W_SDA(1);         // 接收前，主机先确保释放SDA，避免干扰从机的数据发送
  for (i = 0; i < 8; i++) // 循环8次，主机依次接收数据的每一位
  {
    SDA_IN();
    MyI2C_W_SCL(1); // 释放SCL，主机机在SCL高电平期间读取SDA
    if (MyI2C_R_SDA() == 1) {
      Byte |= (0x80 >> i);
    } // 读取SDA数据，并存储到Byte变量
    // 当SDA为1时，置变量指定位为1，当SDA为0时，不做处理，指定位为默认的初值0
    MyI2C_W_SCL(0); // 拉低SCL，从机在SCL低电平期间写入SDA
  }
  return Byte; // 返回接收到的一个字节数据
}

/**
 * 函    数：I2C发送应答位
 * 参    数：Byte 要发送的应答位，范围：0~1，0表示应答，1表示非应答
 * 返 回 值：无
 */
void MyI2C_SendAck(uint8_t AckBit) {
  SDA_OUT();
  MyI2C_W_SDA(AckBit); // 主机把应答位数据放到SDA线
  MyI2C_W_SCL(1);      // 释放SCL，从机在SCL高电平期间，读取应答位
  MyI2C_W_SCL(0);      // 拉低SCL，开始下一个时序模块
}

/**
 * 函    数：I2C接收应答位
 * 参    数：无
 * 返 回 值：接收到的应答位，范围：0~1，0表示应答，1表示非应答
 */
uint8_t MyI2C_ReceiveAck(void) {
  SDA_OUT();
  uint8_t AckBit; // 定义应答位变量
  MyI2C_W_SDA(1); // 接收前，主机先确保释放SDA，避免干扰从机的数据发送
  MyI2C_W_SCL(1); // 释放SCL，主机机在SCL高电平期间读取SDA
  SDA_IN();
  AckBit = MyI2C_R_SDA(); // 将应答位存储到变量里
  MyI2C_W_SCL(0);         // 拉低SCL，开始下一个时序模块
  return AckBit;          // 返回定义应答位变量
}

/**
 * 函    数：MPU6050写寄存器
 * 参    数：RegAddress 寄存器地址，范围：参考MPU6050手册的寄存器描述
 * 参    数：Data 要写入寄存器的数据，范围：0x00~0xFF
 * 返 回 值：无
 */
void GRAY_WriteReg(uint8_t RegAddress, uint8_t Data) {
  MyI2C_Start();                // I2C起始
  MyI2C_SendByte(GRAY_ADDRESS); // 发送从机地址，读写位为0，表示即将写入
  MyI2C_ReceiveAck();           // 接收应答
  MyI2C_SendByte(RegAddress);   // 发送寄存器地址
  MyI2C_ReceiveAck();           // 接收应答
  MyI2C_SendByte(Data);         // 发送要写入寄存器的数据
  MyI2C_ReceiveAck();           // 接收应答
  MyI2C_Stop();                 // I2C终止
}

/**
 * 函    数：MPU6050读寄存器
 * 参    数：RegAddress 寄存器地址，范围：参考MPU6050手册的寄存器描述
 * 返 回 值：读取寄存器的数据，范围：0x00~0xFF
 */
uint8_t GRAY_ReadReg(uint8_t RegAddress) {
  uint8_t Data;

  MyI2C_Start();                // I2C起始
  MyI2C_SendByte(GRAY_ADDRESS); // 发送从机地址，读写位为0，表示即将写入
  MyI2C_ReceiveAck();           // 接收应答
  MyI2C_SendByte(RegAddress);   // 发送寄存器地址
  MyI2C_ReceiveAck();           // 接收应答

  MyI2C_Start();                       // I2C重复起始
  MyI2C_SendByte(GRAY_ADDRESS | 0x01); // 发送从机地址，读写位为1，表示即将读取
  MyI2C_ReceiveAck();                  // 接收应答
  Data = MyI2C_ReceiveByte();          // 接收指定寄存器的数据
  MyI2C_SendAck(1); // 发送应答，给从机非应答，终止从机的数据输出
  MyI2C_Stop();     // I2C终止

  return Data;
}
